package main

import (
	"bufio"
	"fmt"
	"io"
	"net"
)

/**
本程序为内网环境中web服务器所在的主机（或者可连接到内网web服务器同时可以访问外网的间机器）
工作流程：
	1.连接远端服务器ControlAddrPort，接受远端服务器的控制命令
	2.当用户访问远端服务器时，ControlAddrPort传来控制命令"new\n"，
	3.执行combine方法，程序同时拨通TunnelAddrPort 和 ServerAddrPort，
	4.并通过joinConn方法，用io.Copy的方式讲TunnelAddrPort的通信数据和ServerAddrPort的通信数据配对，
实现将内网数据提交到中介服务器ControlAddrPort，再通过中介服务期上的程序实现内网穿透
*/

func main() {
	connectControl()
}

const (
	//中介服务器控制端程序连接地址和端口
	ControlAddrPort = "106.13.84.203:8009"
	//链路实际通信连接地址和端口
	TunnelAddrPort = "106.13.84.203:8008"
	//内网服务程序地址和端口
	ServerAddrPort = "127.0.0.1:22001"
)

//连接到服务器的8009控制端口，随时接受服务器的控制请求，随时待命
func connectControl() {
	var tcpAddr *net.TCPAddr
	//这里在一台机测试，所以没有连接到公网，可以修改到公网ip
	tcpAddr, _ = net.ResolveTCPAddr("tcp", ControlAddrPort)
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		fmt.Println("Client connect error ! " + err.Error())
		return
	}
	fmt.Println(conn.LocalAddr().String() + " : Client connected!8009")
	reader := bufio.NewReader(conn)
	for {
		s, err := reader.ReadString('\n')
		if err != nil || err == io.EOF {
			break
		} else {
			//接收到new的指令的时候，新建一个tcp连接
			if s == "new\n" {
				go combine()
			}
			if s == "hi" {
				//忽略掉hi的请求
			}
		}

	}
}

//combine方法的代码，整合local和remote的tcp连接
func combine() {
	local := connectLocal()
	remote := connectRemote()
	if local != nil && remote != nil {
		joinConn(local, remote)
	} else {
		if local != nil {
			err := local.Close()
			if err != nil {
				fmt.Println("close local:" + err.Error())
			}
		}
		if remote != nil {
			err := remote.Close()
			if err != nil {
				fmt.Println("close remote:" + err.Error())
			}

		}
	}
}
func joinConn(local *net.TCPConn, remote *net.TCPConn) {
	f := func(local *net.TCPConn, remote *net.TCPConn) {
		defer local.Close()
		defer remote.Close()
		_, err := io.Copy(local, remote)
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		fmt.Println("end")
	}
	go f(local, remote)
	go f(remote, local)
}

//connectLocal 连接到内网web服务器！
func connectLocal() *net.TCPConn {
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", ServerAddrPort)

	conn, err := net.DialTCP("tcp", nil, tcpAddr)

	if err != nil {
		fmt.Println("Client connect error ! " + err.Error())
		return nil
	}

	fmt.Println(conn.LocalAddr().String() + " : Client connected!8000")
	return conn

}

//connectRemote 连接到服务端的8008端口！
func connectRemote() *net.TCPConn {
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", TunnelAddrPort)

	conn, err := net.DialTCP("tcp", nil, tcpAddr)

	if err != nil {
		fmt.Println("Client connect error ! " + err.Error())
		return nil
	}
	fmt.Println(conn.LocalAddr().String() + " : Client connected!8008")
	return conn
}
